package com.aop.SpElAop.DistributedLock.RedisLock.support;

/**
 * @Package: com.aop.SpElAop.Lock
 * @ClassName: LockKeyProvider
 * @Author: h2cloud
 * @CreateTime: 2024/7/9 10:06 下午
 * @Description:
 */

import com.aop.SpElAop.DistributedLock.RedisLock.RedisLock;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@Component
public class LockKeyProvider {
	private static final String KEY_PREFIX = "lock";
	private static final String KEY_SEPARATOR = ":";
	private static final String ERROR_NO_VALUE_MESSAGE = "表达式[%s]未获取到值";
	private final ExpressionParser parser = new SpelExpressionParser();
	private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

	public Method getMethod(JoinPoint joinPoint) {
		MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
		return methodSignature.getMethod();
	}

	private String concat(RedisLock distributedLock, String value) {
		Assert.hasText(value, String.format(ERROR_NO_VALUE_MESSAGE, distributedLock.key()));
		return KEY_PREFIX + KEY_SEPARATOR + value;
	}

	public LockInfo getInfo(JoinPoint joinPoint, RedisLock redisLock) {
		Object rootObject = joinPoint.getTarget();
		Method method = getMethod(joinPoint);
		Object[] args = joinPoint.getArgs();
		EvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, args, parameterNameDiscoverer);
		Expression expression = parser.parseExpression(redisLock.key());

		LockInfo lockInfo = new LockInfo();
		lockInfo.setName(concat(redisLock, expression.getValue(context, String.class)));
		lockInfo.setKey(redisLock.key());
		lockInfo.setWaitTime(redisLock.waitTime());
		lockInfo.setLeaseTime(redisLock.leaseTime());
		lockInfo.setUnit(redisLock.unit());
		lockInfo.setLockType(redisLock.lockType());
		lockInfo.setMultiLock(redisLock.isMultiLock());
		lockInfo.setTryLock(redisLock.isTryLock());
		if (redisLock.isMultiLock()) {
			List<?> values = expression.getValue(context, List.class);
			Assert.notNull(values, String.format(ERROR_NO_VALUE_MESSAGE, redisLock.key()));
			List<String> keys = new ArrayList<>();
			values.forEach(value -> keys.add(concat(redisLock, value.toString())));
			lockInfo.setMultiLockNames(keys);
		}
		return lockInfo;
	}
}


